# -*- coding: utf-8 -*-
"""
Created on Tue Nov 11 22:55:17 2025

@author: ymh
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import seaborn as sns
from wordcloud import WordCloud
from sklearn.feature_extraction.text import TfidfVectorizer
import jieba
import re  # 新增：导入正则模块（修复NameError）
import os
import sys

# --------------------------
# 1. 基础配置（修复字体、依赖导入）
# --------------------------
FONT_PATH = "C:/Windows/Fonts/msyh.ttc"
if not os.path.exists(FONT_PATH):
    print(f"错误：字体文件不存在！当前路径：{FONT_PATH}")
    sys.exit(1)

# 配置中文字体
font_prop = fm.FontProperties(fname=FONT_PATH)
plt.rcParams["font.family"] = font_prop.get_name()
plt.rcParams['axes.unicode_minus'] = False
sns.set_style("whitegrid")

# --------------------------
# 2. 数据加载（修复文件路径、列名匹配）
# --------------------------
try:
    # 加载情感分析的原始数据（含评论内容、sentiment列）
    df = pd.read_csv('douban_movie_comments.csv', encoding='utf-8-sig')
    print(f"成功加载原始评论数据，共 {len(df)} 条记录")
    
    # 复用情感分析代码的情感映射逻辑
    df['评分'] = pd.to_numeric(df['评分'], errors='coerce')
    def map_sentiment(score):
        if pd.isna(score):
            return 'neutral'
        elif score <= 2:
            return 'negative'
        elif score == 3:
            return 'neutral'
        else:
            return 'positive'
    df['sentiment'] = df['评分'].apply(map_sentiment)
    
    # 过滤无效评论
    df = df.dropna(subset=['评论内容'])
    df = df[df['评论内容'].str.strip() != '']
    print(f"过滤后有效评论：{len(df)} 条")
    
except FileNotFoundError:
    print("错误：未找到 douban_movie_comments.csv，请确认文件路径正确")
    sys.exit(1)

# --------------------------
# 3. 文本预处理（修复re依赖）
# --------------------------
stopwords = set([
    '的', '了', '是', '在', '这', '都', '让', '我们', '一种', '不是', '没有', '更',
    '但', '与', '中', '为', '用', '当', '于', '以', '将', '成', '对', '自己','很多',
    '就是','什么','最后','《', '》', '：', '——', '、', ',', '.', '！', '？', '看',
    '非常', '很', '也', '还', '就', '有', '把', '给', '说', '你', '我', '她', '他',
    '它', '一个', '一些', '一点','因为','有点','他们',
    '电影', '影片', '觉得', '剧情', '故事', '画面', '演员', '演技', '镜头', '配乐',
    '特效', '导演', '编剧', '类型', '题材', '票房', '上映',
    '观众', '影院', '系列', '续集', '前传', '改编', '原著', '小说', '漫画', '游戏',
    'IP', '角色', '人物', '主角', '配角', '反派','作品','一部',
    '知道', '了解', '认识', '喜欢', '讨厌', '想要', '需要', '应该', '可以', '能够',
    '会', '要', '没', '不', '别', '太', '挺', '比较', '稍微','怎么','还是','还有',
    '那么','这种','这个','这样','但是','这么','感觉','为了','现在','不停','可能'
])

def preprocess(text):
    text = str(text).strip()
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', text)  # 现在re已导入，无报错
    text = re.sub(r'\s+', ' ', text)
    words = jieba.lcut(text)
    valid_words = []
    for word in words:
        if (word.strip() and 
            word not in stopwords and 
            len(word) > 1 and 
            not word.isdigit()):
            valid_words.append(word)
    return valid_words

# 按情感分类文本（使用sentiment列，标签为positive/negative/neutral）
positive_texts = df[df['sentiment'] == 'positive']['评论内容'].tolist()
negative_texts = df[df['sentiment'] == 'negative']['评论内容'].tolist()
neutral_texts = df[df['sentiment'] == 'neutral']['评论内容'].tolist()

# 生成处理后的文本字符串
positive_str = ' '.join([word for text in positive_texts for word in preprocess(text)])
negative_str = ' '.join([word for text in negative_texts for word in preprocess(text)])

# --------------------------
# 4. 可视化1：情感占比图
# --------------------------
def plot_emotion_distribution():
    emotion_counts = df['sentiment'].value_counts()
    label_map = {'positive': '正面', 'negative': '负面', 'neutral': '中性'}
    emotion_counts.index = emotion_counts.index.map(label_map)
    
    plt.figure(figsize=(12, 6), dpi=100)
    plt.subplot(1, 2, 1)
    wedges, texts, autotexts = plt.pie(
        emotion_counts,
        labels=emotion_counts.index,
        autopct='%1.1f%%',
        colors=['#2E8B57', '#DC143C', '#FFD700'],
        startangle=90
    )
    plt.title('情感占比饼图', fontsize=14, fontproperties=font_prop)
    for text in texts:
        text.set_fontproperties(font_prop)
        text.set_fontsize(12)
    for autotext in autotexts:
        autotext.set_fontproperties(font_prop)
        autotext.set_color('white')
        autotext.set_fontsize(12)
    
    plt.subplot(1, 2, 2)
    ax = sns.barplot(x=emotion_counts.index, y=emotion_counts.values, palette=['#2E8B57', '#DC143C', '#FFD700'])
    plt.title('情感数量柱状图', fontsize=14, fontproperties=font_prop)
    plt.xlabel('情感类型', fontproperties=font_prop, fontsize=12)
    plt.ylabel('评论数量', fontproperties=font_prop, fontsize=12)
    plt.xticks(fontproperties=font_prop)
    plt.yticks(fontproperties=font_prop)
    for i, v in enumerate(emotion_counts.values):
        ax.text(i, v + 0.5, str(v), ha='center', fontproperties=font_prop)
    
    plt.tight_layout()
    plt.savefig('情感占比图.png', dpi=300, bbox_inches='tight')
    plt.show()

# --------------------------
# 5. 可视化2：词云图
# --------------------------
def plot_wordclouds():
    plt.figure(figsize=(12, 6), dpi=100)
    
    # 正面词云
    plt.subplot(1, 2, 1)
    if positive_str.strip():
        pos_wc = WordCloud(
            font_path=FONT_PATH,
            background_color='white',
            width=800,
            height=500,
            max_words=50,
            colormap='Greens',
            collocations=False
        ).generate(positive_str)
        plt.imshow(pos_wc, interpolation='bilinear')
    else:
        plt.text(0.5, 0.5, '无正面评论数据', ha='center', va='center', fontproperties=font_prop, fontsize=14)
    plt.axis('off')
    plt.title('正面评论关键词词云', fontsize=14, fontproperties=font_prop)
    
    # 负面词云
    plt.subplot(1, 2, 2)
    if negative_str.strip():
        neg_wc = WordCloud(
            font_path=FONT_PATH,
            background_color='white',
            width=800,
            height=500,
            max_words=50,
            colormap='Reds',
            collocations=False
        ).generate(negative_str)
        plt.imshow(neg_wc, interpolation='bilinear')
    else:
        plt.text(0.5, 0.5, '无负面评论数据', ha='center', va='center', fontproperties=font_prop, fontsize=14)
    plt.axis('off')
    plt.title('负面评论关键词词云', fontsize=14, fontproperties=font_prop)
    
    plt.tight_layout()
    plt.savefig('评论词云图.png', dpi=300, bbox_inches='tight')
    plt.show()

# --------------------------
# 6. 可视化3：TF-IDF关键词图
# --------------------------
def get_tfidf_top_words(texts, top_n=10):
    if not texts or all(str(t).strip() == '' for t in texts):
        return []
    processed = [' '.join(preprocess(text)) for text in texts if str(text).strip()]
    if not processed:
        return []
    tfidf = TfidfVectorizer(max_features=5000)
    tfidf_matrix = tfidf.fit_transform(processed)
    words = tfidf.get_feature_names_out()
    weights = np.sum(tfidf_matrix.toarray(), axis=0)
    return sorted(zip(words, weights), key=lambda x: x[1], reverse=True)[:top_n]

def plot_tfidf_words():
    pos_top = get_tfidf_top_words(positive_texts)
    neg_top = get_tfidf_top_words(negative_texts)
    
    plt.figure(figsize=(12, 6), dpi=100)
    
    # 正面关键词
    plt.subplot(1, 2, 1)
    if pos_top:
        pos_df = pd.DataFrame(pos_top, columns=['关键词', 'TF-IDF权重'])
        sns.barplot(x='TF-IDF权重', y='关键词', data=pos_df, palette='Greens_d')
        plt.title('正面评论Top10关键词（TF-IDF）', fontsize=14, fontproperties=font_prop)
        plt.xlabel('TF-IDF权重', fontproperties=font_prop, fontsize=12)
        plt.ylabel('关键词', fontproperties=font_prop, fontsize=12)
    else:
        plt.text(0.5, 0.5, '无正面评论有效关键词', ha='center', va='center', fontproperties=font_prop, fontsize=12)
    plt.xticks(fontproperties=font_prop)
    plt.yticks(fontproperties=font_prop)
    
    # 负面关键词
    plt.subplot(1, 2, 2)
    if neg_top:
        neg_df = pd.DataFrame(neg_top, columns=['关键词', 'TF-IDF权重'])
        sns.barplot(x='TF-IDF权重', y='关键词', data=neg_df, palette='Reds_d')
        plt.title('负面评论Top10关键词（TF-IDF）', fontsize=14, fontproperties=font_prop)
        plt.xlabel('TF-IDF权重', fontproperties=font_prop, fontsize=12)
        plt.ylabel('关键词', fontproperties=font_prop, fontsize=12)
    else:
        plt.text(0.5, 0.5, '无负面评论有效关键词', ha='center', va='center', fontproperties=font_prop, fontsize=12)
    plt.xticks(fontproperties=font_prop)
    plt.yticks(fontproperties=font_prop)
    
    plt.tight_layout()
    plt.savefig('TF-IDF关键词图.png', dpi=300, bbox_inches='tight')
    plt.show()

# --------------------------
# 7. 可视化4：评分分布箱线图
# --------------------------
def plot_score_distribution():
    score_df = df.dropna(subset=['评分'])
    if len(score_df) == 0:
        print("警告：无有效评分数据，跳过评分分布可视化")
        return
    
    score_df['情感类型'] = score_df['sentiment'].map({'positive': '正面', 'negative': '负面', 'neutral': '中性'})
    
    plt.figure(figsize=(10, 6), dpi=100)
    sns.boxplot(x='情感类型', y='评分', data=score_df, palette=['#2E8B57', '#FFD700', '#DC143C'])
    plt.title('不同情感类型的原始评分分布', fontsize=14, fontproperties=font_prop)
    plt.xlabel('情感类型', fontproperties=font_prop, fontsize=12)
    plt.ylabel('评分（1-5分）', fontproperties=font_prop, fontsize=12)
    plt.xticks(fontproperties=font_prop)
    plt.yticks(fontproperties=font_prop)
    plt.savefig('情感评分分布.png', dpi=300, bbox_inches='tight')
    plt.show()

# --------------------------
# 运行所有可视化
# --------------------------
if __name__ == "__main__":
    print(f"使用字体路径：{FONT_PATH}")
    print("开始生成可视化结果...")
    
    if len(df) == 0:
        print("错误：无有效评论数据，无法生成可视化")
        sys.exit(1)
    
    plot_emotion_distribution()
    plot_wordclouds()
    plot_tfidf_words()
    plot_score_distribution()
    
    print("所有可视化结果已保存为PNG图片！")